#include "BestQuoteWidget.h"
#include "BestQuote/BestQuoteData.h"
#include "BestQuote/BestQuoteProcess.h"
#include "SSTableView/SSTableView.h"
#include <qbkernel/include/qb_base.h>
#include <core/QbLogger.h>
#include <core/StyleMgr.h>
#include <chrono>
#include <QStylePainter>
#include <QFile>
#include <QWindow>
#include <QHeaderView>
#include <QScrollBar>
#include <QStyledItemDelegate>
#include <QTimer>

#if WIN32
#include <Windows.h>
#include <winuser.h>
#else
#include <time.h>
#define min(a,b)            (((a) < (b)) ? (a) : (b))
#define max(a,b)            (((a) > (b)) ? (a) : (b))
#endif

namespace {
    enum eBestQuoteListColumn {
        colCode = 0,
        colName,
        colBidVol,
        colBid,
        colOfr,
        colOfrVol,
        colIssuerRate,
        colBroker,
        colUpdateTime,
        colCount
    };
}

static bool s_data_pushing = false;
class GridItemDelegate : public QStyledItemDelegate
{
public:
    GridItemDelegate(QObject* p);
    virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
};

GridItemDelegate::GridItemDelegate(QObject* p)
    :QStyledItemDelegate(p)
{

}

void GridItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    painter->fillRect(option.rect, QColor(0x10, 0x14, 0x14));
    QStyledItemDelegate::paint(painter, option, index);
    QPen oldPen = painter->pen();
    QPen pen(qRgb(30, 36, 35));
    painter->setPen(pen);
    painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
    painter->setPen(oldPen);
}

QVariant BestQuoteModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == Qt::TextAlignmentRole)
    {
        if (index.column() == 0 || index.column() == 1 || index.column() == 6)
        {
            return int(Qt::AlignLeft | Qt::AlignVCenter);
        }
        else if (index.column() == 7 )
        {
            return int(Qt::AlignCenter | Qt::AlignVCenter);
        }
        else
        {
            return int(Qt::AlignRight | Qt::AlignVCenter);
        }
    }
    else if (role == Qt::DisplayRole)
    {
          return QStandardItemModel::data(index, role);
    }
    else if(role == Qt::BackgroundRole)
    {
        QColor color(0x10, 0x14, 0x14);
        if (m_flashModel)
        {
            float percent = m_flashModel->flashPercent(data(index, Qt::UserRole).toLongLong());
            if (percent > 0)
            {
                color = QColor(65, 50, 23);
                color.setAlphaF(percent);
            }
       }
        return color;
    }
    else if(role == Qt::ForegroundRole)
    {
        if (index.column() == 4)
        {
            return QVariant(QColor(0,153,253));
        }
        else
        {
            return QVariant(QColor(249, 193, 82));
        }
    }

    return QStandardItemModel::data(index, role);
}

QVariant BestQuoteModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role == Qt::TextAlignmentRole)
    {
        if (section == 0 || section == 1 || section == 6)
        {
            return int(Qt::AlignLeft | Qt::AlignVCenter);
        }
        else if (section == 7 )
        {
            return int(Qt::AlignCenter | Qt::AlignVCenter);
        }
        else
        {
            return int(Qt::AlignRight | Qt::AlignVCenter);
        }
    }

    return QStandardItemModel::headerData(section, orientation, role);
}
/*
void BestQuoteModel::sort(int column, Qt::SortOrder order)
{
    beginResetModel();
    endResetModel();
}*/

BestQuoteWidget::BestQuoteWidget(const QString& title, QWidget* parent)
    : QWidget(parent)
//     , m_mainLayout(NULL)
    , m_bestquoteItemModel(NULL)
    , m_tableviewBestQuote(NULL)
    , m_vertScrollBar(NULL)
    , m_pushIdx(0)
    , m_maxRowCount(40)//TODO:根据可视范围
	, m_pIndexMap(NULL)
    , m_process(NULL)
{
//	m_ui.setupUi(this);
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    connect(this, &BestQuoteWidget::destroyed, this, &BestQuoteWidget::OnDestroy);

    initCtrl();
    initData();
}

BestQuoteWidget::~BestQuoteWidget()
{
    delete m_flash;


    if (m_process)
    {
        delete m_process;
        m_process = NULL;
    }

    BestQuoteData::GetInstance().UnInitData();
}

void BestQuoteWidget::initCtrl()
{
//     m_mainLayout = new QHBoxLayout(this);       //垂直布局
//     m_mainLayout->setSpacing(0);         //设置控件间距
//     m_mainLayout->setContentsMargins(0, 0, 0, 0);          //设置边缘间距

    createTabBroker();
    createQuoteView();
}

void BestQuoteWidget::initData()
{
}

void BestQuoteWidget::createTabBroker()
{
/*	QFile file(":/qb/tabButton.qss");
	bool ret = file.open(QFile::ReadOnly);
	QByteArray qdata = file.readAll();

	m_tabBroker = new QButtonGroup;
	for (int i = 0; i <= 5; i++)
	{
		QPushButton* pBtn = NULL;
		switch (i)
		{
		case 0: pBtn = m_ui.btnAll; pBtn->setText("全部"); break;
		case 1: pBtn = m_ui.btnTP; pBtn->setText("国利"); break;
		case 2: pBtn = m_ui.btnICAP; pBtn->setText("国际"); break;
		case 3: pBtn = m_ui.btnBGC; pBtn->setText("中诚"); break;
		case 4: pBtn = m_ui.btnPA; pBtn->setText("平安"); break;
		case 5: pBtn = m_ui.btnXT; pBtn->setText("信唐"); break;
		default: break;
		}
		pBtn->setStyleSheet(qdata);
        pBtn->setCheckable(true);

		m_tabBroker->addButton(pBtn, i);
	}
	m_tabBroker->setExclusive(false);//单选
	m_tabBroker->button(0)->setChecked(true);*/

    connect(m_tabBroker, &QButtonGroup::idClicked, this, &BestQuoteWidget::OnTabBrokerClick);
}

void BestQuoteWidget::createQuoteView()
{
    m_tableviewBestQuote = new SSTableView(this);
    m_flash = new ItemFlashModel(m_tableviewBestQuote);
    m_bestquoteItemModel = new BestQuoteModel();
    m_bestquoteItemModel->setParent(this);//构造函数
    m_bestquoteItemModel->setFlashModel(m_flash);

	connect((SSTableView*)m_tableviewBestQuote, &SSTableView::mouseWheel, this, &BestQuoteWidget::OnTableViewMouseWheel);
    //添加表头
    m_bestquoteItemModel->setColumnCount(colCount);

	m_bestquoteItemModel->setHorizontalHeaderItem(colCode, new QStandardItem(QObject::tr("代码")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colName, new QStandardItem(QObject::tr("简称")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colBidVol, new QStandardItem(QObject::tr("Vol.Bid")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colBid, new QStandardItem(QObject::tr("Bid")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colOfr, new QStandardItem(QObject::tr("Ofr")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colOfrVol, new QStandardItem(QObject::tr("Vol.Ofr")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colIssuerRate, new QStandardItem(QObject::tr("主/债")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colBroker, new QStandardItem(QObject::tr("经纪商")));
	m_bestquoteItemModel->setHorizontalHeaderItem(colUpdateTime, new QStandardItem(QObject::tr("最后更新")));

    for (int row = 0; row < m_maxRowCount; row++)
    {
        for (int col = 0; col < colCount; col++)
        {
            m_bestquoteItemModel->setItem(row, col, new QStandardItem(""));
        }
    }

	m_tableviewBestQuote->setModel(m_bestquoteItemModel);    //挂载表格模型
    m_tableviewBestQuote->verticalHeader()->setDefaultSectionSize(24);
    //设置表头可点击
	QHeaderView* headerView = m_tableviewBestQuote->horizontalHeader();
	connect(headerView, &QHeaderView::sectionClicked, this, &BestQuoteWidget::OnClickHeader);
    headerView->setSectionsClickable(true);
    //隐藏QTableView自带滚动条
    m_tableviewBestQuote->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    headerView->setStyleSheet("QHeaderView::section {"
                                                   "background-color: #1E2423;color: rgb(19,108,94); padding-left: 4px;padding-right: 4px;height:24px;border-right:1px solid #101414}");
    headerView->setDefaultAlignment(Qt::AlignRight);        //表头信息显示居中

    for (int i = 0; i < colCount; i++)
    {
        headerView->setSectionResizeMode(i, QHeaderView::Stretch);  //设定表头列宽不可变
    }

    //设置表格属性
    m_tableviewBestQuote->setStyleSheet("QTableView::item{selection-background-color:rgb(106,63,39)}");
    m_tableviewBestQuote->setShowGrid(false);
    m_tableviewBestQuote->verticalHeader()->hide();    //隐藏默认显示的行头
    m_tableviewBestQuote->setSelectionBehavior(QAbstractItemView::SelectRows); //设置选中时整行选中
    m_tableviewBestQuote->setEditTriggers(QAbstractItemView::NoEditTriggers);  //设置表格属性只读，不能编辑
    m_tableviewBestQuote->setItemDelegate(new GridItemDelegate(this));
//    m_tableviewBestQuote->setSortingEnabled(true);
//    m_tableviewBestQuote->sortByColumn(7, Qt::DescendingOrder);

    //m_ui.layoutView->addWidget(m_tableviewBestQuote, 0, );

	//m_ui.layoutQuote->addWidget(m_tableviewBestQuote);    //添加控件

	m_vertScrollBar = new QScrollBar(this);

    QString strFile(":/qb/scrollBar.qss");
    if (StyleMgr::StyleType::kBlue == StyleMgr::instance().getColorStyle()) {
        strFile = ":/qb/scrollBar_blue.qss";
    }

	QFile file(strFile);
	bool ret = file.open(QFile::ReadOnly);
	QByteArray qdata = file.readAll();
    m_vertScrollBar->setStyleSheet(qdata);

	connect(m_vertScrollBar, &QScrollBar::sliderMoved, this, &BestQuoteWidget::OnScrollBarChange);

 //   m_ui.layoutQuote->addWidget(m_vertScrollBar);    //添加控件

//    this->setLayout(m_mainLayout);        //显示垂直布局

    resetScrollBar();
}

void BestQuoteWidget::resetScrollBar(bool bReset)
{
    int pos = m_vertScrollBar->sliderPosition();
	BestQuoteData& bqData = BestQuoteData::GetInstance();
	int rowCount = bqData.m_result.size();
	if (rowCount > m_maxRowCount)
	{
        if (bReset)pos = 0;
        //else pos = fmax(0, min(pos, rowCount - m_maxRowCount));
		m_vertScrollBar->setRange(0, rowCount);
		m_vertScrollBar->setPageStep(m_maxRowCount);
		m_vertScrollBar->setSliderPosition(pos);
        m_vertScrollBar->setEnabled(true);
	}
    else
	{
		m_vertScrollBar->setRange(0, 0);
		m_vertScrollBar->setSliderPosition(pos);
        m_vertScrollBar->setDisabled(true);
    }
}

void BestQuoteWidget::updateView()
{
	auto startTime = std::chrono::system_clock::now();

    BestQuoteData& bqData = BestQuoteData::GetInstance();
    int sz = bqData.m_result.size();
    int rowCount = min(sz, m_maxRowCount);
	int row = 0;
    int pos = m_vertScrollBar->sliderPosition();
    for (; row < rowCount; row++)
    {
        int iRes = row + pos;
        if (iRes < 0 || iRes >= (int)bqData.m_result.size())continue;
        int index = bqData.m_result[iRes];
        if (index < 0 || index >= (int)bqData.m_index.size())continue;

		BondIndexBroker bk = bqData.m_index[index];
        if (bk.reportIndex < 0 || bk.reportIndex >= (int)bqData.m_quotedata.size())continue;

        const REPORT2& rpt = bqData.m_quotedata[bk.reportIndex];
/*        const BondInfo bi = m_pBondList->at(bk.bondIndex);

		QString sCode = codec->toUnicode(bi.code.c_str());
		QString sName = codec->toUnicode(bi.name.c_str());
		QString sRate = codec->toUnicode(bi.level.c_str());
		QString sBid = codec->toUnicode(rpt.m_bidinfo.m_sPrice);
		QString sBidVol = codec->toUnicode(rpt.m_bidinfo.m_sVolume);
		QString sOfr = codec->toUnicode(rpt.m_askinfo.m_sPrice);
		QString sOfrVol = codec->toUnicode(rpt.m_askinfo.m_sVolume);
        QString sBroker;
        switch (bk.brokerId)
        {
		case 1: sBroker = "国利"; break;
		case 2: sBroker = "国际"; break;
		case 3: sBroker = "中诚"; break;
		case 4: sBroker = "平安"; break;
		case 5: sBroker = "信唐"; break;
        default: break;
        }
        struct tm stm;
        struct tm* pTime = &stm;
#ifdef WIN32
        gmtime_s(pTime, &(rpt.m_time));
#else
        pTime = gmtime(&(rpt.m_time));
#endif
        int hour = pTime->tm_hour + 8;
        if (hour >= 24)hour -= 24;
		QString sTime = QString("%1:%2:%3").arg(hour, 2, 10,QLatin1Char('0')).
            arg(pTime->tm_min, 2, 10,QLatin1Char('0')).
            arg(pTime->tm_sec, 2, 10, QLatin1Char('0'));

        for (int col = colCode; col < colCount; col++)
		{
            QString itemText;
            switch (col)
			{
            case colCode: itemText = sCode; break;
            case colName: itemText = sName; break;
			case colBidVol: itemText = sBidVol; break;
			case colBid: itemText = sBid; break;
			case colOfr: itemText = sOfr; break;
			case colOfrVol: itemText = sOfrVol; break;
			case colIssuerRate: itemText = sRate; break;
			case colBroker: itemText = sBroker; break;
			case colUpdateTime: itemText = sTime; break;
            default: break;
            }
			QStandardItem* pItem = m_bestquoteItemModel->item(row, col);
            if (!pItem)
                pItem = new QStandardItem(itemText);
            else
                pItem->setText(itemText);
            m_bestquoteItemModel->setItem(row, col, pItem);
            pItem->setData(((qint64)bk.brokerId << 32) | qint64(bk.bondIndex), Qt::UserRole);
        }*/
	}

    for (; row < m_maxRowCount; row++)
	{
		for (int col = colCode; col < colCount; col++)
		{
			QStandardItem* pItem = m_bestquoteItemModel->item(row, col);
			if (!pItem)
				pItem = new QStandardItem((""));
			else
				pItem->setText((""));
			m_bestquoteItemModel->setItem(row, col, pItem);
		}
    }

	auto endTime = std::chrono::system_clock::now();
	//QB_TRACE(qb::kTraceDebug, kTraceUserFace, 0, "%s cost %dms", __FUNCTION__, std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count());
}

void BestQuoteWidget::OnTabBrokerClick(int id)
{
/*  if (!m_tabBroker)return;
    for (int i = 0; i <= 5; i++)
    {
        QAbstractButton* pBtn = m_tabBroker->button(i);
        if (!pBtn)continue;
		if (i == id)
			pBtn->setChecked(true);
        else
            pBtn->setChecked(false);
    }

    BestQuoteData& bqData = BestQuoteData::GetInstance();
    BestQuoteMiniFilter& flt = bqData.GetFilter();
    if (flt.m_nBrokerId == id)return;

	flt.m_nBrokerId = id;
	auto T0 = std::chrono::system_clock::now();
    bqData.ReloadData();
	auto T1 = std::chrono::system_clock::now();
	log_debug("%s BestQuoteData::ReloadData result size = %d, cost %dms", __FUNCTION__, bqData.m_result.size(), std::chrono::duration_cast<std::chrono::milliseconds>(T1 - T0).count());

    bqData.Sort();
	auto T2 = std::chrono::system_clock::now();
    log_debug("%s BestQuoteData::Sort cost %dms", __FUNCTION__, std::chrono::duration_cast<std::chrono::milliseconds>(T2 - T1).count());

    updateView();
	auto T3 = std::chrono::system_clock::now();
    log_debug("%s BestQuoteWidget::updateView cost %dms", __FUNCTION__, std::chrono::duration_cast<std::chrono::milliseconds>(T3 - T2).count());*/
}

void BestQuoteWidget::OnClickHeader(int col)
{
    BestQuoteData& bqData = BestQuoteData::GetInstance();
    if (bqData.m_result.size() <= 0)return;
    
    eBestQuoteSortKey sortKey = (eBestQuoteSortKey)(col + 1);
    if (sortKey <= eSort_None || sortKey >= eSort_count)return;

    if (bqData.GetSortKey() == sortKey)
	{
        bqData.SetSortDirection(!bqData.GetSortDirection());
    }
    else
    {
        bqData.SetSortKey(sortKey);
        bqData.SetSortDirection(false);
    }
	bqData.Sort();
    resetScrollBar(true);
    updateView();
}

void BestQuoteWidget::OnTableViewMouseWheel(int delta)
{
    if (!m_tableviewBestQuote || !m_vertScrollBar)return;
    int pos = m_vertScrollBar->sliderPosition();
    int page = m_vertScrollBar->pageStep();
    int step = m_vertScrollBar->singleStep();
    int nMax = m_vertScrollBar->maximum();
    if (delta > 0)
    {
        pos -= step;
#ifdef WIN32
        pos = fmax(0, pos);
#else
        pos = max(0, pos);
#endif
    }
    else
	{
		pos += step;
        pos = min(pos, nMax - page);
    }

	m_vertScrollBar->setSliderPosition(pos);
	updateView();
}


void BestQuoteWidget::OnScrollBarChange(int position)
{
    updateView();
}

void BestQuoteWidget::OnFreeAckData(void* pData)
{
	if (pData)
	{
	}
}

void BestQuoteWidget::OnDataProcessFinish(void* pData)
{
	auto T0 = std::chrono::system_clock::now();

	BestQuoteData::GetInstance().UpdateData(pData);
	auto T1 = std::chrono::system_clock::now();
    log_debug("%s BestQuoteData::UpdateData total %d records, cost %dms", __FUNCTION__, BestQuoteData::GetInstance().m_index.size(), std::chrono::duration_cast<std::chrono::milliseconds>(T1 - T0).count());

	BestQuoteData::GetInstance().Sort();
	auto T2 = std::chrono::system_clock::now();
    log_debug("%s BestQuoteData::Sort cost %dms", __FUNCTION__, std::chrono::duration_cast<std::chrono::milliseconds>(T2 - T1).count());

	if (pData)
	{
		BestQuoteProcessData* pd = (BestQuoteProcessData*)pData;
		for (int i = 0; i < (int)pd->count; ++i)
		{
			m_flash->addFlashData(pd->pbib[i], i == pd->count - 1);
		}
		delete[]pd->pbib;
		delete[]pd->prpt;
		delete pd;
	}

	resetScrollBar();
	updateView();
	auto T3 = std::chrono::system_clock::now();
    log_debug("%s BestQuoteWidget::updateView cost %dms", __FUNCTION__, std::chrono::duration_cast<std::chrono::milliseconds>(T3 - T2).count());
    log_debug("%s delete memory", __FUNCTION__);
}

void BestQuoteWidget::OnDestroy()
{
    if (m_process)
    {
        delete m_process;
        m_process = NULL;
    }

    BestQuoteData::GetInstance().UnInitData();
}

void BestQuoteWidget::updateData(void* pData)
{
    QB_CHECK_RETURN_VOID1(m_process)
    m_process->processData(pData);
}

ItemFlashModel::ItemFlashModel(QTableView* t)
    : m_table(t)
{
    m_timer = new QTimer();
    m_timer->callOnTimeout([&]() {
        update();
        });
}

ItemFlashModel::~ItemFlashModel()
{
    if (m_timer)
    {
        m_timer->stop();
        delete m_timer;
    }
}

void ItemFlashModel::addFlashData(BondIndexBroker bi, bool end)
{
    if (!s_data_pushing)
        return;
    qint64 key = ((qint64)bi.brokerId << 32) | (qint64)bi.bondIndex;
    m_flashData[key] = 15;
    if(!m_timer->isActive())
        m_timer->start(200);
    if(end && m_countTimer.elapsed() > 200)
        update();
}

float ItemFlashModel::flashPercent(qint64 key) const
{
    auto iter = m_flashData.find(key);
    if (iter != m_flashData.end())
        return iter->second / 15.0;
    return -1;
}

void ItemFlashModel::update()
{
	if (m_flashData.empty() || !m_table)
		return;
	auto iter = m_flashData.begin();
	for (; iter != m_flashData.end();)
	{
		if (--(iter->second) <= 0)
		{
			iter = m_flashData.erase(iter);
		}
		else
			++iter;
	}

    emit m_table->model()->dataChanged(m_table->rootIndex().child(0, 0), m_table->rootIndex().child(19, 7));
    if (m_flashData.empty())
        m_timer->stop();

    m_countTimer.restart();
}
